
#include <BPainter>
#include <BKeyEvent>
#include <BMouseEvent>

#include "PixmapArea.h"
#include "CutoffOperation.h"

CutoffOperation::CutoffOperation() : Operation("CutoffOperation")
{
	_hoverPoint.set(-1, -1);
	_layout = new BHBoxLayout();
	{
		_applyButton = new BButton("apply");
		_cancelButton = new BButton("cancel");
		_layout->addWidget(_applyButton);
		_layout->addWidget(_cancelButton);
	}
	connect(_applyButton, Signal_Clicked, &CutoffOperation::slotApplyButtonClicked);
	connect(_cancelButton, Signal_Clicked, &CutoffOperation::slotCancelButtonClicked);
}
CutoffOperation::~CutoffOperation()
{

}

void CutoffOperation::freshPartRects()
{
	if (_selectRect.valid())
	{
		int b = 5;
		_dragRectMap[Part_None] = _selectRect;
		_dragRectMap[Part_Left] = BRect(-b, b, b + b, _selectRect.height() - b - b) + _selectRect.pos();
		_dragRectMap[Part_Top] = BRect(b, -b, _selectRect.width() - b - b, b + b) + _selectRect.pos();
		_dragRectMap[Part_Right] = BRect(_selectRect.width() - b, b, b + b, _selectRect.height() - b - b) + _selectRect.pos();
		_dragRectMap[Part_Bottom] = BRect(b, _selectRect.height() - b, _selectRect.width() - b - b, b + b) + _selectRect.pos();
		_dragRectMap[Part_Center] = BRect(b, b, _selectRect.width() - b - b, _selectRect.height() - b - b) + _selectRect.pos();
		_dragRectMap[Part_LeftTop] = BRect(-b, -b, b + b, b + b) + _selectRect.pos();
		_dragRectMap[Part_LeftBottom] = BRect(-b, _selectRect.height() - b, b + b, b + b) + _selectRect.pos();
		_dragRectMap[Part_RightTop] = BRect(_selectRect.width() - b, -b, b + b, b + b) + _selectRect.pos();
		_dragRectMap[Part_RightBottom] = BRect(_selectRect.width() - b, _selectRect.height() - b, b + b, b + b) + _selectRect.pos();
	}
	else
	{
		_dragRectMap.reset();
	}
}
void CutoffOperation::applyPartDraging(const BPoint& pos)
{
	BPoint offset = pos - _dragPoint;
	_selectRect = _dragRectMap[Part_None];
	if (_dragPart == Part_Center)
	{
		_selectRect += offset;
		return;
	}
	if (_dragPart & Part_Left)
	{
		_selectRect.left() += offset.x();
		return;
	}
	if (_dragPart & Part_Top)
	{
		_selectRect.top() += offset.y();
		return;
	}
	if (_dragPart & Part_Right)
	{
		_selectRect.right() += offset.x();
		return;
	}
	if (_dragPart & Part_Bottom)
	{
		_selectRect.bottom() += offset.y();
		return;
	}
}

void CutoffOperation::slotApplyButtonClicked()
{
	BRect rect = this->pixmapArea()->mapToPixmap(_selectRect);
	if (BImage* image = this->pixmapArea()->image())
	{
		const BSize& size = image->size();
		BImage* newImage = new BImage(rect.size(), Format_RGBA);
		for (int r = 0; r < rect.height(); r++)
		{
			BByte* imageBytes = image->bytes(r + rect.y()) + rect.x() * 4;
			BByte* bytes = newImage->bytes(r);
			memcpy(bytes, imageBytes, rect.width() * 4);
		}
		emit("insert-layer", newImage);
	}
}
void CutoffOperation::slotCancelButtonClicked()
{
	emit("operation-escape", this);
}

void CutoffOperation::attached(BWidget* widget)
{
	widget->addLayout(_layout);
	Operation::attached(widget);
}
void CutoffOperation::detached(BWidget* widget)
{
	widget->removeLayout(_layout);
	Operation::detached(widget);
}

void CutoffOperation::paint(BPainter& painter)
{
	PixmapArea* pixmapArea = this->pixmapArea();
	BRect area = pixmapArea->area();
	BPixmap* pixmap = pixmapArea->pixmap();
	if (_hoverPoint > BPoint(-1, -1))
	{
		float sw = area.width() / (float)pixmap->width();
		float sh = area.height() / (float)pixmap->height();
		int x = int(sw * _hoverPoint.x()) + area.x();
		int y = int(sh * _hoverPoint.y()) + area.y();
		painter.setColor(255, 0, 0);
		if (sw > 1 && sh > 1)
			painter.drawRect(x, y, (int)sw, (int)sh);
		else
			painter.drawPoint(x, y);
	}
	if (_selectRect.valid())
	{
		painter.setLineStipple(0x0F0F);
		painter.setColor(255, 0, 0);
		painter.drawRect(_selectRect);
		painter.setLineStipple(0);
	}

	//for (auto it = _dragRectMap.begin(); it.valid(); ++it)
	//{
	//	painter.drawRect(*it);
	//}
}
void CutoffOperation::update(double time)
{

}

void CutoffOperation::freshEvent(const BEvent& event)
{
	BSize hsize = _layout->sizeHint();
	BRect crect = this->pixmapArea()->centerRect();
	BRect layoutRect = crect.align(Align_RightBottom, hsize);
	_layout->setRect(layoutRect);
	_layout->adjust();

}

bool CutoffOperation::focusEvent(const BEvent& event)
{
	return false;
}
bool CutoffOperation::keyDownEvent(const BKeyEvent& keyEvent)
{
	if (_selectRect.valid() && keyEvent.key() == Key_Escape)
	{
		_selectRect.reset();
		_dragRectMap.reset();
		return true;
	}
	return false;
}
bool CutoffOperation::keyUpEvent(const BKeyEvent& keyEvent)
{
	return false;
}

bool CutoffOperation::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (_hoverPoint != BPoint(-1, -1))
	{
		_hoverPoint.set(-1, -1);
		this->pixmapArea()->fresh();
	}
	return false;
}
bool CutoffOperation::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (_hoverPoint != BPoint(-1, -1))
	{
		_hoverPoint.set(-1, -1);
		this->pixmapArea()->fresh();
	}
	return false;
}

bool CutoffOperation::mousePressEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	if (mouseEvent.button() == Button_Left)
	{
		if (_dragPart)
		{
			_draging = true;
			_dragPoint = mouseEvent.pos();
			return true;
		}
		PixmapArea* pixmapArea = this->pixmapArea();
		BRect area = pixmapArea->area();
		BPoint point = area.wrap(pos);
		_pressPoint = point;
		_selectRect.reset(point);
		pixmapArea->fresh();
		return true;
	}

	return false;
}
bool CutoffOperation::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (_draging)
	{
		_draging = false;
		_dragPart = Part_None;
		freshPartRects();
		this->fresh();
	}
	return false;
}
bool CutoffOperation::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	PixmapArea* pixmapArea = this->pixmapArea();
	BRect area = pixmapArea->area();
	if (mouseEvent.button() == Button_Left)
	{
		if (_draging && _dragPart)
		{
			applyPartDraging(pos);
			pixmapArea->fresh();
			return true;
		}
		BPoint point = area.wrap(pos);
		_selectRect.reset(_pressPoint);
		_selectRect.expand(pos);
		freshPartRects();
		pixmapArea->fresh();
		return true;
	}
	if (area.contain(pos))
	{
		BPoint point = pixmapArea->mapToPixmap(pos);
		if (_hoverPoint != point)
		{
			_hoverPoint = point;
			pixmapArea->fresh();
		}
	}
	float ratio = pixmapArea->zoom() / 100.0f;
	_dragPart = Part_None;
	for (auto it = _dragRectMap.begin(); it < _dragRectMap.end(); it++)
	{
		if (it.key() > Part_None && it->contain(pos))
		{
			_dragPart = it.key();
			break;
		}
	}
	if (_dragPart == Part_None)
		pixmapArea->setCursor(Cursor_None);
	else if (_dragPart == Part_Center)
		pixmapArea->setCursor(Cursor_SizeAll);
	else if (_dragPart == Part_Left || _dragPart == Part_Right)
		pixmapArea->setCursor(Cursor_SizeHor);
	else if (_dragPart == Part_Top || _dragPart == Part_Bottom)
		pixmapArea->setCursor(Cursor_SizeVer);
	else if (_dragPart == (Part_Left | Part_Top))
		pixmapArea->setCursor(Cursor_SizeLeftTop);
	else if (_dragPart == (Part_Left | Part_Bottom))
		pixmapArea->setCursor(Cursor_SizeLeftBottom);
	else if (_dragPart == (Part_Right | Part_Top))
		pixmapArea->setCursor(Cursor_SizeRightTop);
	else if (_dragPart == (Part_Right | Part_Bottom))
		pixmapArea->setCursor(Cursor_SizeRightBottom);

	return true;
}
bool CutoffOperation::mouseWheelEvent(const BMouseEvent& mouseEvent)
{
	return false;
}

bool CutoffOperation::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	return false;
}
bool CutoffOperation::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	return false;
}
